import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, linear_model
from sklearn.metrics import accuracy_score
from sklearn import datasets
from sklearn.datasets import load_digits

def sigmoid(x):
    res = 1 / (1 + np.exp(-x))
    return res

class NN_BP:
    def __init__(self, nodes, X, label):
        self.nodes = nodes
        self.eta = 0.001
        self.n_epoch = 5000
        self.X = X
        self.label = np.zeros((np.shape(X)[0],nodes[-1]))
        # 构建分类矩阵，正确位置为1，错误位置为0
        for i in range(self.nodes[-1]):
            self.label[np.where(label==i),i] = 1
        # 权重初始化
        self.W = []
        self.B = []
        self.output = []
        for i in range(len(self.nodes)-1):
            W_elem = np.random.rand(self.nodes[i],self.nodes[i+1])
            self.W.append(W_elem)
            B_elem = np.random.rand(1,self.nodes[i+1])
            self.B.append(B_elem)

    # 构建正向传播函数
    def forward(self, X):
        x = X
        self.output = []
        for i in range(len(self.nodes)-1):
            x = sigmoid(np.dot(x,self.W[i]) + self.B[i])
            self.output.append(x)
        res = np.argmax(self.output[-1], axis = 1)
        return res

    # 构建反向传播函数
    def backward(self):
        for i in range(self.n_epoch):
            self.forward(self.X)
            D = []
            for_num = len(self.nodes)-1
            for j in range(for_num,0,-1):
                if j == len(self.nodes)-1:
                    delta = self.output[j-1] * (1-self.output[j-1]) * (self.label-self.output[j-1])
                else:
                    delta = self.output[j-1] * (1-self.output[j-1]) * np.dot(delta,self.W[j].T)
                D.insert(0,delta)
            for j in range(for_num,0,-1):
                if j == 1:
                    self.W[j-1] += self.eta * np.dot(self.X.T,D[j-1])
                else:
                    self.W[j-1] += self.eta * np.dot(self.output[j-2].T,D[j-1])
                self.B[j-1] += self.eta * np.sum(D[j-1],axis = 0)
            self.evaluate()

    def evaluate(self):
        L = np.sum((self.output[-1]-self.label)**2)
        y_pre = np.argmax(self.output[-1], axis = 1)
        y_true = np.argmax(self.label, axis = 1)
        acc = accuracy_score(y_true, y_pre)
        print("L= %f,正确率为 %f" % (L,acc))


# circle
data = np.loadtxt("F:\\dataset_circles.csv", delimiter = ",")
train_data = data[:,0:2]
# 以第三列为测试集
true_data = np.squeeze(data[:,2])
nn = NN_BP([2,8,8,2],train_data,true_data)
nn.backward()
data_pre = nn.forward(train_data)
plt.figure(1)
plt.scatter(train_data[:,0].T,train_data[:,1].T,c = true_data)
plt.title('true result')
plt.figure(2)
plt.scatter(train_data[:,0].T,train_data[:,1].T,c = data_pre)
plt.title('test result')